#ifndef MODEL_H
#define MODEL_H

#include "Constants.h"

#include <GL/glew.h>
#include <GL/gl.h>

#include <glm/glm.hpp>

#include <vector>
#include <string>

using glm::vec2;
using glm::vec3;
using std::string;
using std::vector;

struct BufferObjectData
{
    GLuint vao;
    GLuint element;
    GLuint vertex;
    GLuint normal;
    GLuint uv;
    GLuint tangent;
    GLuint bitangent;
    GLsizei size;

    BufferObjectData() : vao(0), element(0), vertex(0), normal(0), uv(0), size(0)
    {
    }
};

struct Vertex
{
    vec3 pos;
    vec3 normal;
    vec2 uv;

    Vertex() : pos(0.0, 0.0, 0.0), normal(0.0, 0.0, 0.0), uv(0.0, 0.0)
    {
    }

    Vertex(vec3 p, vec3 n, vec2 u) : pos(p), normal(n), uv(u)
    {
    }

    bool operator<(const Vertex that) const
    {
        return memcmp((void*) this, (void*)& that, sizeof(Vertex)) > 0;
    }
};

class Model
{
public:

    // Functions.
    explicit Model(const string& filename);
    ~Model();

    GLuint getVAO() const;
    string getName() const;
    GLsizei getBufferObjectSize() const;

private:

    // Disable copying.
    Model(Model const& rhs);
    Model&operator=(Model const& rhs);

    // Functions.
    bool loadObject();
    void computeTangentBasis();
    void indexVBO();
    void setVertexAttributes();

    template <class T, class U>
    void SetAttributeData(T& data, GLuint bufferNum, int size, GLuint attrib);

    // Variables.
    string m_filename;

    vector<vec3> m_vertices;
    vector<vec3> m_normals;
    vector<vec2> m_uvs;
    vector<vec3> m_tangents;
    vector<vec3> m_bitangents;
    vector<unsigned short> m_indices;

    BufferObjectData m_bufferObject;
};

#endif // MODEL_H